#ifndef __SYLAR_LOG_H__
#define __SYLAR_LOG_H__

#include <string>
#include <stdint.h>
#include <memory>
#include <list>
#include <sstream>
#include <fstream>
#include <vector>
#include <stdarg.h>
#include <map>
#include "util.h"
#include "singleton.h"
#include "thread.h"

// 记录日志，并且返回 ss，让用户可以使用流的形式写入日志
#define SYLAR_LOG_LEVEL(logger, level) \
  if(logger->getLevel() <= level) \
      sylar::LogEventWrap(sylar::LogEvent::ptr(new sylar::LogEvent(logger, level, \
            __FILE__, __LINE__, 0, sylar::GetThreadId(), sylar::Thread::GetName(), \
        sylar::GetFiberId(), time(0)))).getSS()

// 提供用户可能使用的 api
#define SYLAR_LOG_DEBUG(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::DEBUG)
#define SYLAR_LOG_INFO(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::INFO)
#define SYLAR_LOG_WARN(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::WARN)
#define SYLAR_LOG_ERROR(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::ERROR)
#define SYLAR_LOG_FATAL(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::FATAL)

// 让用户自主设置格式
// 缺陷：不能使用流写入日志，使用形式与上面不统一
#define SYLAR_LOG_FMT_LEVEL(logger, level, fmt, ...) \
   if(level >= logger->getLevel()) \
       sylar::LogEventWrap(sylar::LogEvent::ptr(new sylar::LogEvent(logger, level, \
             __FILE__, __LINE__, 0, sylar::GetThreadId(), sylar::Thread::GetName(), \
         sylar::GetFiberId(), time(0)))).getEvent()->format(fmt, __VA_ARGS__)

// 提供用户可能使用的 api
#define SYLAR_LOG_FMT_DEBUG(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::DEBUG, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_INFO(logger, fmt, ...)  SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::INFO, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_WARN(logger, fmt, ...)  SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::WARN, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_ERROR(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::ERROR, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_FATAL(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::FATAL, fmt, __VA_ARGS__)

// 方便用户使用的 api，获取跟 logger
#define SYLAR_LOG_ROOT() sylar::LoggerMgr::GetInstance()->getRoot()
// 获取日志名为 name 的 logger
#define SYLAR_LOG_NAME(name) sylar::LoggerMgr::GetInstance()->getLogger(name)

namespace sylar {

class Logger;
class LoggerManager;

// 日志级别
class LogLevel {
public:
  enum Level {
    UNKNOW = 0,
    DEBUG = 1,
    INFO = 2,
    WARN = 3,
    ERROR = 4,
    FATAL = 5
  };

  static const char* ToString(LogLevel::Level level);
  // 通过 string 获得日志级别
  static LogLevel::Level FromString(const std::string& str);
};

// 日志事件
class LogEvent {
public:
  typedef std::shared_ptr<LogEvent> ptr;
  LogEvent(std::shared_ptr<Logger> logger, LogLevel::Level level
      ,const char* file, int32_t m_line, uint32_t elapse
      , uint32_t thread_id, std::string threadName
      , uint32_t fiber_id, uint64_t time);

  const char* getFile() const { return m_file;}
  int32_t getLine() const { return m_line;}
  uint32_t getElapse() const { return m_elapse;}
  uint32_t getThreadId() const { return m_threadId;}
  const std::string& getThreadName() const { return m_threadName; }
  uint32_t getFiberId() const { return m_fiberId;}
  uint64_t getTime() const { return m_time;}
  // 获取格式化之后的日志事件
  std::string getContent() const { return m_ss.str();}
  std::shared_ptr<Logger> getLogger() const { return m_logger;}
  LogLevel::Level getLevel() const { return m_level;}

  std::stringstream& getSS() { return m_ss;}
  // format 调用函数(函数入口)
  void format(const char* fmt, ...);
  // format 实际实现功能的函数, 按照 fmt 格式格式化日志事件，并输出到 m_ss
  void format(const char* fmt, va_list al);
private:
  // 文件名
  const char* m_file = nullptr;
  // 行号
  int32_t m_line = 0;
  // 程序启动开始到现在的毫秒数
  uint32_t m_elapse = 0;
  // 线程id
  uint32_t m_threadId = 0;
  // 线程名
  std::string m_threadName = "";
  // 协程id
  uint32_t m_fiberId = 0;
  // 时间戳
  uint64_t m_time = 0;

  // 用来存储格式化之后的日志事件
  std::stringstream m_ss;
  // 该日志事件的级别
  LogLevel::Level m_level;
  // 该日志事件输出的日志
  std::shared_ptr<Logger> m_logger;
};

// 日志事件包装器
class LogEventWrap {
public:
  LogEventWrap(LogEvent::ptr e);
  // 析构时执行 log 方法
  ~LogEventWrap();
  LogEvent::ptr getEvent() const { return m_event;}
  // 获取 m_event 的 m_ss
  std::stringstream& getSS();
private:
  LogEvent::ptr m_event;
};

// 日志格式器
class LogFormatter {
public:
  typedef std::shared_ptr<LogFormatter> ptr;
  LogFormatter(const std::string& pattern);

  // 解析 pattern， 并向 items 中按序添加解析得到的 item
  void init();
  // %t    %thread_id %m%n
  // 对 event 进行格式化
  // pattern 解析完成之后通过调用解析得到的每一个具体项中的 item->format 实现
  std::string format(LogEvent::ptr event);
  bool isError() const { return m_error;  }
  const std::string getPattern() const { return m_pattern;}
public:
  // 一条日志中的一些具体项（logEvent 中成员变量的包装以及换行等不可见字符）
  class FormatItem {
  public:
    typedef std::shared_ptr<FormatItem> ptr;
    virtual ~FormatItem() {}
    // 向 os 中输出具体的 item
    virtual void format(std::ostream& os, LogEvent::ptr event) = 0;
  };

private:
  // 格式
  std::string m_pattern;
  // 通过解析 pattern 获得的具体输出项
  std::vector<FormatItem::ptr> m_items;
  // 判断格式是否错误
  bool m_error = false;
};

// 日志输出地
class LogAppender {
  friend class Logger;
public:
  typedef std::shared_ptr<LogAppender> ptr;
  typedef Spinlock MutexType;
  virtual ~LogAppender() {}

  // 将 event 写入 appender
  virtual void log(LogEvent::ptr event) = 0;
  virtual std::string toYamlString() = 0;

  // 由于方法会修改 formater，且 formater 是个复杂类型，多线程时如果不加锁则中间状态可能出问题
  void setFormatter(LogFormatter::ptr val);
  LogFormatter::ptr getFormatter();

  // 由于 level 是基本类型，当数据不一致时不会造成严重问题。故可以不加锁
  LogLevel::Level getLevel() const { return m_level;}
  void setLevel(LogLevel::Level val) { m_level = val;}
protected:
  LogLevel::Level m_level = LogLevel::DEBUG;
  bool m_hasFormatter = false;
  // 互斥量
  MutexType m_mutex;
  // 不同的 appender 可以有不同的 formatter
  LogFormatter::ptr m_formatter;
};

// 日志器
class Logger : public std::enable_shared_from_this<Logger> {
  friend class LoggerManager;
public:
  typedef std::shared_ptr<Logger> ptr;
  typedef Spinlock MutexType;

  Logger(const std::string& name = "root");
  // 内部调用 appender.log(level, event)
  void log(LogEvent::ptr event);

  void addAppender(LogAppender::ptr appender);
  void delAppender(LogAppender::ptr appender);
  void clearAppenders();
  LogLevel::Level getLevel() const { return m_level;}
  void setLevel(LogLevel::Level val) { m_level = val;}

  const std::string& getName() const { return m_name;}

  void setFormatter(LogFormatter::ptr val); 
  void setFormatter(const std::string& val);

  LogFormatter::ptr getFormatter();

  std::string toYamlString();
private:
  // 日志名称
  std::string m_name;
  // 默认日志级别
  LogLevel::Level m_level;       
  // 互斥量
  MutexType m_mutex;
  // Appender集合
  std::list<LogAppender::ptr> m_appenders;
  // 日志的默认格式，当添加的 appender 没有 formatter 时使用
  LogFormatter::ptr m_formatter;
  // 指向该 logger 的父节点, 让 logger 形成类似树的结构
  // 当当前 logger 没有 appender 时，将 logEvent 写入 m_root
  Logger::ptr m_root;
};

// 输出到控制台的Appender
class StdoutLogAppender : public LogAppender {
public:
  typedef std::shared_ptr<StdoutLogAppender> ptr;
  void log(LogEvent::ptr event) override;
  std::string toYamlString() override;
};

//定义输出到文件的Appender
class FileLogAppender : public LogAppender {
public:
  typedef std::shared_ptr<FileLogAppender> ptr;
  FileLogAppender(const std::string& filename);
  void log(LogEvent::ptr event) override;
  std::string toYamlString() override;

  //重新打开文件，文件打开成功返回true
  bool reopen();
private:
  std::string m_filename;
  std::ofstream m_filestream;
  // 记录上一次的写入时间，以秒为单位
  uint64_t m_lastTime = 0;
};

// logger 管理器
class LoggerManager {
public:
  typedef Spinlock MutexType;
  LoggerManager();
  // 通过 name 获取 logger，如果不存在则创建一个
  Logger::ptr getLogger(const std::string& name);

  Logger::ptr getRoot() const { return m_root;}

  std::string toYamlString();
private:
  // logger name 到 logger 的映射
  std::map<std::string, Logger::ptr> m_loggers;
  // 根 logger
  Logger::ptr m_root;
  MutexType m_mutex;
};

typedef sylar::Singleton<LoggerManager> LoggerMgr;

}

#endif
